using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Studio.Foundation.ImageBox
{

    public partial class PickBox : Form
    {

        #region Type defination

        enum PickActions
        {
            NONE,
            PICKING,
            MOVING,
            RESIZING,   // Resizing the selection from right-bottom conner
            MOVEPANEL
        }

        enum PickProcessing
        { 
            None,
            Gray,
            Binary
        }
        
        public delegate void PickBoxCallBack();


        #endregion 

        #region Constructor, Varibles & Properties

        /// <summary>
        /// Default constructor
        /// </summary>
        public PickBox()
        {
            InitializeComponent();

            // Initialize members
            _Rect = Rectangle.Empty;
            _Action = PickActions.NONE;
            _CallBack = null;
        
        }


        private void PickBox_Load(object sender, EventArgs e)
        {
            // Initialize screen shot
            _Selection = PixEye.Snapshoot();
            this.BackgroundImage = _Selection;
            // Set processing method comboBox
            comboBoxProcessing.Items.Clear();
            foreach (string Method in Enum.GetNames(typeof(PickProcessing))) comboBoxProcessing.Items.Add(Method);
            comboBoxProcessing.SelectedIndex = 0;
            pictureBoxSelection.Visible = false;
        }

        private Rectangle _Rect;
        /// <summary>
        /// Save selected Rectangle by mouse
        /// </summary>
        public Rectangle Rect
        {
            get { return _Rect; }
        }

        private Point _StartPos;

        protected Form _Sender; 
        /// <summary>
        /// Save the one who called me.
        /// </summary>
        public Form Sender
        {
            get { return _Sender; }
            set { _Sender = value; }
        }

        protected PickBoxCallBack _CallBack = null;
        /// <summary>
        /// Call this delegate while this.Closed
        /// </summary>
        public PickBoxCallBack CallBack
        {
            get { return _CallBack; }
            set { _CallBack = value; }
        }

        /// <summary>
        /// Indicate what is the current operation
        /// </summary>
        private PickActions _Action;

        private Bitmap _Selection;
        /// <summary>
        /// Selected Rectangle
        /// </summary>
        public Bitmap Selection
        {
            get { return _Selection; }
        }

        #endregion

        #region Event Drivers

        /// <summary>
        /// Close and Save Image
        /// </summary>
        private void PickBox_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            this.Close();

        }

        private void pictureBoxSelection_DoubleClick(object sender, EventArgs e)
        {
            this.Close();
        }

        private void PickBox_MouseDown(object sender, MouseEventArgs e)
        {
            // Left button?
            if (e.Button == MouseButtons.Left)
            {
                // Save start position first
                _StartPos = e.Location;
                // Calculate Positive Position
                _StartPos.Offset(((Control)sender).Location);
                if (Rectangle.Empty == _Rect)
                {
                    // Start to Pick
                    _Action = PickActions.PICKING;
                    _Rect = new Rectangle(_StartPos, new Size(0, 0));
                }
                else if (_Rect.Contains(_StartPos))
                {
                    // Move or Resize selected Rectangle?
                    _Action = PickActions.MOVING;
                }
            }
            else if (e.Button == MouseButtons.Right)
            {
                // Right button to cancel selection
                if(Rectangle.Empty != _Rect) Cancel();
            }
        }

        private void PickBox_MouseMove(object sender, MouseEventArgs e)
        {
            // Calculate Positive Position
            Point o = e.Location;
            o.Offset(((Control)sender).Location);
            
            switch (_Action)
            {
                case PickActions.PICKING:   // select a rectangle
                    // Hide Selection PictureBox
                    pictureBoxSelection.Visible = false;
                    _Rect.X = Math.Min(_StartPos.X, o.X);
                    _Rect.Y = Math.Min(_StartPos.Y, o.Y);
                    _Rect.Width = Math.Abs(_StartPos.X - o.X);
                    _Rect.Height = Math.Abs(_StartPos.Y - o.Y);
                    break;
                case PickActions.MOVING:    // move selected rect
                    // Hide Selection PictureBox
                    pictureBoxSelection.Visible = false;
                    _Rect.Offset(o.X - _StartPos.X, o.Y - _StartPos.Y);
                    _StartPos = o;
                    break;
                case PickActions.RESIZING:  // resize selected rect
                    // Hide Selection PictureBox
                    pictureBoxSelection.Visible = false;
                    break;
            }

            // Update UI
            if (_Rect.Contains(o))
                this.Cursor = Cursors.SizeAll;
            else
                this.Cursor = Cursors.Default;
            this.Refresh();
            UpdatePanel(sender, new MouseEventArgs(e.Button, e.Clicks, o.X, o.Y, e.Delta));
        }


        private void PickBox_MouseUp(object sender, MouseEventArgs e)
        {
            _Action = PickActions.NONE;
            // Draw selection 
            if(false == pictureBoxSelection.Visible) SaveSelection();
        }

        /// <summary>
        /// Paint selection rectange
        /// </summary>
        private void PickBox_Paint(object sender, PaintEventArgs e)
        {
            if (Rectangle.Empty != _Rect)
                e.Graphics.DrawRectangle(new Pen(Brushes.OrangeRed), _Rect);
        }

        private void panelInfo_MouseDown(object sender, MouseEventArgs e)
        {
            _Action = PickActions.MOVEPANEL;
            _StartPos = e.Location + new Size(panelInfo.Left, panelInfo.Top);
        }

        private void panelInfo_MouseMove(object sender, MouseEventArgs e)
        {
            MouseEventArgs me = new MouseEventArgs(e.Button, e.Clicks, e.X + panelInfo.Left, e.Y + panelInfo.Top, e.Delta);
            UpdatePanel(sender, me);

            if (PickActions.MOVEPANEL == _Action)
            {
                // Move Information Panel
                panelInfo.Left += me.X - _StartPos.X;
                panelInfo.Top += me.Y - _StartPos.Y;
                _StartPos = me.Location;
            }
            this.Refresh();
        }

        private void panelInfo_MouseUp(object sender, MouseEventArgs e)
        {
            _Action = PickActions.NONE;
        }

        private void linkLabelRefresh_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            this.Hide();
            System.Threading.Thread.Sleep(100); // Sleep & wait the desktop's refreshing
            this.BackgroundImage = PixEye.Snapshoot();
            this.Show();
        }

        private void PickBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Escape) Cancel();
            // Slight move
            if (Rectangle.Empty != _Rect)
            {
                switch (e.KeyCode)
                {
                    case Keys.Up: _Rect.Y--; SaveSelection(); break;
                    case Keys.Down: _Rect.Y++; SaveSelection(); break;
                    case Keys.Left: _Rect.X--; SaveSelection(); break;
                    case Keys.Right: _Rect.X++; SaveSelection(); break;
                }
                this.Refresh();
            }
        }

        private void linkLabelCancel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            Cancel();
        }

        private void PickBox_FormClosing(object sender, FormClosingEventArgs e)
        {
            // Restore the caller form
            if (null != _Sender)
            {
                _Sender.Show();
                _Sender.WindowState = FormWindowState.Normal;
            }
            // Save selected area
            // SaveSelection();
        }

        private void linkSave_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            SaveSelection();
            PixEye.SaveImage(_Selection);
        }

        private void linkLabelRect_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            ResizeRect();
        }


        private void textBoxRect_KeyDown(object sender, KeyEventArgs e)
        {
            if (Keys.Enter == e.KeyCode) ResizeRect();
        }


        private void pictureBoxMagnify_Paint(object sender, PaintEventArgs e)
        {
            PictureBox Picture = sender as PictureBox;
            // Draw Vertical Lines
            for (int i = 0; i <= Picture.Width; i += 4) e.Graphics.DrawLine(Pens.Silver, i, 0, i, Picture.Height);
            // Draw Horizotal Lines
            for (int i = 0; i <= Picture.Height; i += 4) e.Graphics.DrawLine(Pens.Silver, 0, i, Picture.Width, i);
        }

        private void pictureBoxSelection_Paint(object sender, PaintEventArgs e)
        {
            PictureBox Picture = sender as PictureBox;
            // Draw Borders
            e.Graphics.DrawRectangle(Pens.OrangeRed, new Rectangle(0, 0, Picture.Width, Picture.Height));
        }

        private void comboBoxProcessing_SelectedIndexChanged(object sender, EventArgs e)
        {
            SaveSelection();
        }


        #endregion

        #region Utilities

        private void ResizeRect()
        {
            // Parse new Value
            int Left, Top, Width, Height;
            int.TryParse(textBoxLeft.Text, out Left);
            int.TryParse(textBoxTop.Text, out Top);
            int.TryParse(textBoxWidth.Text, out Width);
            int.TryParse(textBoxHeight.Text, out Height);
            _Rect = new Rectangle(Left, Top, Width, Height);
            // Repaint Form & pictureBox
            this.Refresh();
            pictureBoxSelection.Refresh();
        }

        private void UpdatePanel(object sender, MouseEventArgs e)
        {
            // Update panel informations and position
            labelRectangle.Text = string.Format("Rectangle ({0}, {1}) - ({2}, {3}) [{4}, {5}]",
                _Rect.Top, _Rect.Left, _Rect.Bottom, _Rect.Right, _Rect.Width, _Rect.Height);
            // Mouse Position
            labelMouse.Text = string.Format("Mouse ({0}, {1})", e.X, e.Y);
            // Magnified Bitmap
            pictureBoxMagnify.Width = 64;
            pictureBoxMagnify.Height = 64;
            int w = pictureBoxMagnify.Width / 4;
            int h = pictureBoxMagnify.Height / 4;
            Bitmap Magnify = PixEye.Snapshoot(new Rectangle(e.X - w / 2, e.Y - h / 2, w, h));
            pictureBoxMagnify.Image = Magnify;
            // Color From Cursor
            Color c = Magnify.GetPixel(w / 2, h / 2);
            pictureBoxSample.BackColor = c;
            labelColor.Text = string.Format("Color ({0}, {1}, {2})", c.R, c.G, c.B);

            textBoxTop.Text = _Rect.Top.ToString();
            textBoxLeft.Text = _Rect.Left.ToString();
            textBoxWidth.Text = _Rect.Width.ToString();
            textBoxHeight.Text = _Rect.Height.ToString();

            /*
            // Auto Move panel Like QQ
            int Margin = 30;

            panelInfo.Top = Margin;
            if (e.Y < panelInfo.Top + panelInfo.Height)
            {
                if (e.X <= panelInfo.Width + Margin)
                    panelInfo.Left = Screen.PrimaryScreen.Bounds.Width - panelInfo.Width - Margin;
                if (e.X >= (Screen.PrimaryScreen.Bounds.Width - panelInfo.Width - Margin))
                    panelInfo.Left = Margin;
            }
            */
        }

        protected void Cancel()
        {
            if (Rectangle.Empty != _Rect)   // Cancel selection
            {
                _Rect = Rectangle.Empty;
                pictureBoxSelection.Visible = false;
            }
            else
                this.Close();               // Cancel window
            _Action = PickActions.NONE;
            this.Refresh();
        }

        protected void SaveSelection()
        {
            if (Rectangle.Empty != _Rect)
            {
                // Hide useless control
                panelInfo.Visible = false;
                pictureBoxSelection.Visible = false;
                // Save rectangle
                Rectangle Rect = _Rect;
                // Clear _Rect
                _Rect = Rectangle.Empty;
                this.Refresh();
                // Save Selection
                _Selection = PixEye.Snapshoot(Rect);
                _Rect = Rect;
                // Show processed Image in picture box
                switch ((PickProcessing)Enum.Parse(typeof(PickProcessing), comboBoxProcessing.Text))
                { 
                    case PickProcessing.None:
                        pictureBoxSelection.Image = _Selection;
                        break;
                    case PickProcessing.Gray:
                        pictureBoxSelection.Image = PixEye.Gray(_Selection, GrayBox.GrayMethod.Average);
                        break;
                    case PickProcessing.Binary:
                        pictureBoxSelection.Image = PixEye.QuickThreshold(_Selection);
                        break;
                }
                pictureBoxSelection.Size = _Rect.Size;
                pictureBoxSelection.Location = _Rect.Location;
                // Refresh UI
                pictureBoxSelection.Visible = true;
                panelInfo.Visible = true;
            }
        }

        #endregion 

    }
}